#define NOMINMAX
#include "BondDetailPrimarySimilarData.h"
#include <uam/UserAccountManager.h>
#include <bondlib/QBIssuerInfo.h>
#include <bondlib/BondCalendar.h>
#include <core/time/system_time.h>
#include <bitset>
#include <map>
#include <vector>
#include <mutex>
#include <thread>
#include <core/Utility.h>
CPrimarySimilarEx::CPrimarySimilarEx() :INVALID_BROKEDR_ID(-1)
{
	m_bInitial = false;
	m_vctBroker.clear();
	m_pDispatchMessageThread = nullptr;
}

CPrimarySimilarEx::~CPrimarySimilarEx()
{
	if (m_pDispatchMessageThread != nullptr)
	{
		m_bStopDispatch = true;
	/*	m_pDispatchMessageThread->interrupt();*///中断某一线程
		m_pDispatchMessageThread->join();

		delete m_pDispatchMessageThread;
		m_pDispatchMessageThread = nullptr;

		{
			std::lock_guard<std::mutex> lock(m_mutex_queue);
			std::queue<SimilarCacheKey> emptyQueue;
			std::swap(emptyQueue, m_queue_query);
		}
	}
}

void CPrimarySimilarEx::initialBroker()
{
	const CBrokerList::BrokerMap& bm = SSUserAccountManager::instance().GetReadonlyBroker().GetBrokerMap();
	int nIndex = 0;
	for (auto itr = bm.begin(); itr != bm.end(); ++itr)
	{
		const CBrokerUnit& stUnit = itr->second;
		if (!CBrokerList::IsValidBroker(stUnit.m_brokerID))
			continue;

		if (!stUnit.m_enableFlag_BOND && !stUnit.m_enableFlag_NCD)
			continue;

		m_mapBroker.insert(std::pair<int, CBrokerUnit>(nIndex, stUnit));
		nIndex++;
	}
}

void CPrimarySimilarEx::initial()
{
	initialBroker();
	m_bInitial = true;
}

int CPrimarySimilarEx::loadSimilarEx(SimilarCacheKey& sk, std::vector<BrokerKey>& vec_idxs, std::vector<int>& vec_idx)
{
	if (sk.sCombondkey.size() <= 0)
		return emKeyIsEmpty;

	CBondContainer& bc = CBondContainer::instance();
	int bondIndex = bc.GetBondIndex(sk.sCombondkey.c_str());
	if (!bc.IsValidIndex(bondIndex))
		return emNotFindBond;

	int nRet = emNotCache;

	if (isHaveCache(sk, m_map_cache, m_rw_cache))
	{
		std::unique_lock<std::mutex> wlock(m_rw_cache);
		auto itr_cache_find = m_map_cache.find(sk);
		vec_idxs = itr_cache_find->second;

		auto itr_cache_idx_find = m_map_idx_cache.find(sk);
		vec_idx = itr_cache_idx_find->second;
		m_map_cache.erase(itr_cache_find);
		m_map_idx_cache.erase(itr_cache_idx_find);
		nRet = emSuccess;
	}
	else
	{
		if (!isQuering(sk))
		{
			insertMapQueryData(sk);
			if (m_pDispatchMessageThread == nullptr)
				m_pDispatchMessageThread = new std::thread(std::bind(&CPrimarySimilarEx::threadProc, this));

			std::lock_guard<std::mutex> lock(m_mutex_queue);
			m_queue_query.push(sk); //std::queue<SimilarCacheKey>
			m_condition_pop.notify_one();
		}
		nRet = emNotCache;
	}
	return nRet;
}

bool CPrimarySimilarEx::isQuering(SimilarCacheKey sck)
{
	std::lock_guard<std::mutex> rlock(m_rw_quering);

	bool bRet = true;
	auto itr_quering = m_map_quering.find(sck); //std::map<SimilarCacheKey, bool>
	if (itr_quering == m_map_quering.end())
		bRet = false;

	return bRet;
}

bool CPrimarySimilarEx::isHaveCache(SimilarCacheKey sck, std::map<SimilarCacheKey, std::vector<BrokerKey>>& map_cache, std::mutex& rw_lock_r)
{
	std::lock_guard<std::mutex>  rlock(rw_lock_r);

	auto itr_cache_find = map_cache.find(sck);

	return itr_cache_find != map_cache.end();
}

void CPrimarySimilarEx::threadProc()
{
	while (!m_bStopDispatch)
	{
		SimilarCacheKey iNode = pop();

		if (iNode.sCombondkey.size() <= 0)
			continue;

		CBondContainer& bondContainer = CBondContainer::instance();
		int bondIndex = bondContainer.GetBondIndex(iNode.sCombondkey.c_str());
		if (bondContainer.IsValidIndex(bondIndex))
		{
			const CBondInfo& biIno = bondContainer.ElementAtR(bondIndex);
			CSSVContainer& btSource = CSSVContainer::availableBond();

			BrokerKey stBondKey;

			std::vector<int> vctCurrentBond;
			std::vector<BrokerKey> vctCurrentBondValid;

			int nIndex = -1;
			int nSize = btSource.GetSize();
			for (int i = 0; i < nSize; i++)
			{
				const CBondInfo& bi = btSource.ElementAt2(i);
				if (!bi.IsValidBond())
					continue;

				if (strcmp(bi.GetCombBondKey(), iNode.sCombondkey.c_str()) == 0) //当前券和债券容器每一条券的COMBOKEY进行比较
					continue;

				if (isValidBond(iNode, &bi, biIno))
				{
					nIndex = CSSVContainer::availableBond().ElementAt(i);
					if (iNode.nType == _RELEVANCEFILTERSECOND || iNode.nType == _RELEVANCEFILTERISSUER)
					{
						vctCurrentBond.push_back(nIndex);
					}
					else
					{
						stBondKey.SetBrokerId("-1");
						stBondKey.m_nIndex = nIndex;
						vctCurrentBondValid.push_back(stBondKey);
					}
				}
			}

			if (iNode.nType == _RELEVANCEFILTERSECOND)
			{
				reGetSecondVaildData(vctCurrentBond, vctCurrentBondValid);
			}
			else if (iNode.nType == _RELEVANCEFILTERISSUER)
			{
				reGetIssuerVaildData(biIno, vctCurrentBond, vctCurrentBondValid);
			}

			if (iNode.nType < _RELEVANCEFILTERISSUER || iNode.nType > _RELEVANCEFILTERFIRST)
			{
				log_info("NEW DETAIL RELEVANCE FILTER ERROR TYPE : %d", iNode.nType);
				return;
			}

			{
				std::unique_lock<std::mutex> wlock(m_rw_cache);
				m_map_cache[iNode] = vctCurrentBondValid;
				m_map_idx_cache[iNode] = vctCurrentBond;
			}
		}

		{
			std::unique_lock<std::mutex> wLock(m_rw_quering);
			std::map<SimilarCacheKey, bool>::iterator itr_quering = m_map_quering.find(iNode);
			if (itr_quering != m_map_quering.end())
				m_map_quering.erase(itr_quering);
		}

		doBroadcast(iNode);
	}
}

void CPrimarySimilarEx::doBroadcast(SimilarCacheKey& sck)
{
	srand((int)time(nullptr));
	int nKey = rand();
	{
		std::unique_lock<std::mutex> wlock(m_rw_notify_key);
		m_map_notify.insert(std::make_pair(nKey, sck));
	}
	emit notifyGetSimilarBond(nKey);
}

void CPrimarySimilarEx::reGetIssuerVaildData(const CBondInfo& bi_src, std::vector<int>& vctCurrentBond, std::vector<BrokerKey>& vctCurrentBondValid)
{
	vctCurrentBondValid.clear();

	BrokerKey stBondKey;

	std::map<std::string, int> mapTempKey;

	char cErrorBroker[12] = { 0 };
	FMTBUF(cErrorBroker, "%d", INVALID_BROKEDR_ID);

	bool bBrokerMobileOnline = SSUserAccountManager::instance().IsBrokerMobileOnline();

	SCOPED_MUTEX(CMarketStream::bondStream().GetMutex());

	int nSz = vctCurrentBond.size();
	for (int nb = 0; nb < nSz; nb++)
	{
		CBondInfo* pInfo = CSSVContainer::availableBond().GetBond(vctCurrentBond[nb]);

		if (!pInfo || !pInfo->IsValidBond())
			continue;

		if (strcmp(bi_src.GetBondKey(), pInfo->GetBondKey()) == 0)
			continue;

		if (mapTempKey.find(pInfo->GetBondKey()) != mapTempKey.end())
			continue;

		mapTempKey[pInfo->GetBondKey()] = 1;
		if (bBrokerMobileOnline)
		{
			stBondKey.SetBrokerId(cErrorBroker);
			stBondKey.m_nIndex = vctCurrentBond[nb];
			vctCurrentBondValid.push_back(stBondKey);
			continue;
		}

		if (insertIssuerWndKey(pInfo->GetBondKey(), stBondKey))
		{
			vctCurrentBondValid.push_back(stBondKey);
		}
	}
}

bool CPrimarySimilarEx::insertIssuerWndKey(const char* bondKey, BrokerKey& stBondKey)
{
	if (strlen(bondKey) <= 0)
		return false;
	if (getMarketReport(bondKey, CBondContainer::lmCIB, stBondKey))
		return true;
	else if (getMarketReport(bondKey, CBondContainer::lmSSE, stBondKey))
		return true;
	else if (getMarketReport(bondKey, CBondContainer::lmSZE, stBondKey))
		return true;

	CBondInfo bondInfo;
	stBondKey.SetBrokerId("-1");
	int bondIndex = -1;
	for (int i = 0; i < 3; i++)
	{
		if (i == 0)bondIndex = CBondContainer::instance().GetBondIndex(bondKey, CBondContainer::lmCIB);
		else if (i == 1)bondIndex = CBondContainer::instance().GetBondIndex(bondKey, CBondContainer::lmSSE);
		else if (i == 2)bondIndex = CBondContainer::instance().GetBondIndex(bondKey, CBondContainer::lmSZE);
		if (CBondContainer::instance().IsValidIndex(bondIndex))break;
		bondIndex = -1;
	}
	if(CBondContainer::instance().IsValidIndex(bondIndex))
	{
		stBondKey.m_nIndex = bondIndex;
		return true;
	}
	return true;
}

bool CPrimarySimilarEx::getMarketReport(const char* bondKey, const char* cMarket, BrokerKey& stBondKey)
{
	stBondKey.Clear();

	REPORT rpt;

	char cBroker[12] = { 0 };
	char cErrorBroker[12] = { 0 };
	FMTBUF(cErrorBroker, "%d", INVALID_BROKEDR_ID);

	bool bHave, bDone, bBest;//是否有报价，没有就加入空行

	MarketInfo marketInfo;

	bDone = bBest = bHave = false;

	time_t tTemp = 0;

	int bondIndex = CBondContainer::instance().GetBondIndex(bondKey, cMarket);
	if (!CBondContainer::instance().IsValidIndex(bondIndex))return false;
	const CBondInfo& bondInfo = CBondContainer::instance().ElementAtR(bondIndex);

	stBondKey.m_nIndex = bondIndex;
	for (auto itUnit = m_mapBroker.begin(); itUnit != m_mapBroker.end(); ++itUnit)
	{
		FMTBUF(cBroker, "%d", itUnit->second.m_brokerID);
		memset(&rpt, 0, sizeof(REPORT));
		bBest = CBondContainer::instance().GetCompanyRepByKey(bondInfo.GetCombBondKey(), cBroker, rpt);
		bDone = CMarketStream::bondStream().GetMarketStreamByKey(bondInfo.GetCombBondKey(), cBroker, marketInfo);

		if (bBest)
		{
			if (atoi(rpt.m_bidinfo.m_price_status) != 0 && strlen(rpt.m_bidinfo.m_sPrice) > 0)
			{
				if (!bHave || std::max(rpt.m_bidinfo.m_create_time, rpt.m_bidinfo.m_time) > tTemp)
				{
					stBondKey.SetBrokerId(cBroker);
					tTemp = std::max(rpt.m_bidinfo.m_create_time, rpt.m_bidinfo.m_time);
				}
				bHave = true;
			}
			if (atoi(rpt.m_askinfo.m_price_status) != 0 && strlen(rpt.m_askinfo.m_sPrice) > 0)
			{
				if (!bHave || std::max(rpt.m_askinfo.m_create_time, rpt.m_askinfo.m_time) > tTemp)
				{
					stBondKey.SetBrokerId(cBroker);
					tTemp = std::max(rpt.m_bidinfo.m_create_time, rpt.m_bidinfo.m_time);
				}
				bHave = true;
			}
		}
		if (bDone && strlen(marketInfo.m_body.m_price) > 0)
		{
			if (!bHave || std::max(marketInfo.m_create_time, marketInfo.m_modify_time) > tTemp)
			{
				stBondKey.SetBrokerId(cBroker);
				tTemp = std::max(rpt.m_bidinfo.m_create_time, rpt.m_bidinfo.m_time);
			}
			bHave = true;
		}
	}
	return bHave;
}

void CPrimarySimilarEx::reGetSecondVaildData(std::vector<int>& vctCurrentBond, std::vector<BrokerKey>& vctCurrentBondValid)
{
	vctCurrentBondValid.clear();
	int nSz = vctCurrentBond.size();

	CBondInfo* pInfo = nullptr;
	REPORT rpt;

	char cBroker[12] = { 0 };
	char cErrorBroker[12] = { 0 };
	FMTBUF(cErrorBroker, "%d", INVALID_BROKEDR_ID);
	bool bHave, bDone, bBest;//是否有报价，没有就加入空行
	bool bBrokerMobileOnline = SSUserAccountManager::instance().IsBrokerMobileOnline();

	MarketInfo marketInfo;
	BrokerKey stBondKey;
	SCOPED_MUTEX(CMarketStream::bondStream().GetMutex());
	for (int nb = 0; nb < nSz; nb++)
	{
		pInfo = CSSVContainer::availableBond().GetBond(vctCurrentBond[nb]);
		if (!pInfo || !pInfo->IsValidBond())
			continue;

		if (bBrokerMobileOnline)
		{
			stBondKey.SetBrokerId(cErrorBroker);
			stBondKey.m_nIndex = vctCurrentBond[nb];
			vctCurrentBondValid.push_back(stBondKey);
			continue;
		}

		bDone = bBest = bHave = false;

		for (auto itUnit = m_mapBroker.begin(); itUnit != m_mapBroker.end(); ++itUnit)
		{
			FMTBUF(cBroker, "%d", itUnit->second.m_brokerID);
			memset(&rpt, 0, sizeof(REPORT));
			bBest = CBondContainer::instance().GetCompanyRepByKey(pInfo->GetCombBondKey(), cBroker, rpt);
			bDone = CMarketStream::bondStream().GetMarketStreamByKey(pInfo->GetCombBondKey(), cBroker, marketInfo);
			if (bBest)
			{
				if (atoi(rpt.m_bidinfo.m_price_status) != 0 && strlen(rpt.m_bidinfo.m_sPrice) > 0)
				{
					bHave = true;
					stBondKey.SetBrokerId(cBroker);
					stBondKey.m_nIndex = vctCurrentBond[nb];
					vctCurrentBondValid.push_back(stBondKey);
					continue;
				}

				if (atoi(rpt.m_askinfo.m_price_status) != 0 && strlen(rpt.m_askinfo.m_sPrice) > 0)
				{
					bHave = true;
					stBondKey.SetBrokerId(cBroker);
					stBondKey.m_nIndex = vctCurrentBond[nb];
					vctCurrentBondValid.push_back(stBondKey);
					continue;
				}
			}
			if (bDone && strlen(marketInfo.m_body.m_price) > 0)
			{
				bHave = true;
				stBondKey.SetBrokerId(cBroker);
				stBondKey.m_nIndex = vctCurrentBond[nb];
				vctCurrentBondValid.push_back(stBondKey);
				continue;
			}
		}
		if (!bHave)
		{
			stBondKey.SetBrokerId(cErrorBroker);
			stBondKey.m_nIndex = vctCurrentBond[nb];
			vctCurrentBondValid.push_back(stBondKey);
		}
	}
}

bool CPrimarySimilarEx::loadNotifyCombondKey(int nRandKey, SimilarCacheKey& sk)
{
	bool bRet = false;
	std::lock_guard<std::mutex> rlock(m_rw_notify_key);

	std::map<int, SimilarCacheKey>::iterator  itr_notify = m_map_notify.find(nRandKey);
	if (itr_notify != m_map_notify.end())
	{
		sk = itr_notify->second;
		bRet = true;
	}
	return bRet;
}

CPrimarySimilarEx::SimilarCacheKey CPrimarySimilarEx::pop()
{
	SimilarCacheKey iNode;
	{
		std::lock_guard<std::mutex> lock(m_mutex_queue);
		while (m_queue_query.empty())
			m_condition_pop.wait(m_mutex_queue); //阻塞等待通知信号

		iNode = m_queue_query.front();
		m_queue_query.pop();
	}
	return iNode;
}

void CPrimarySimilarEx::insertMapQueryData(SimilarCacheKey sck)
{
	std::lock_guard<std::mutex> locker(m_rw_quering);
	m_map_quering.insert(std::make_pair(sck, true));  //map<SimilarCacheKey, bool>
}

CPrimarySimilarEx& CPrimarySimilarEx::GetInstance()
{
	static CPrimarySimilarEx iSimilar;

	if (!iSimilar.getInitFlag())
		iSimilar.initial();

	return iSimilar;
}

bool CPrimarySimilarEx::isValidBond(SimilarCacheKey& sck, const CBondInfo* pInfo, const CBondInfo& currentInfo)
{
	if (!pInfo || !pInfo->IsValidBond())
		return false;

	if (sck.nType == _RELEVANCEFILTERISSUER)
	{
		return isValidIssuer(pInfo, currentInfo);
	}
	else if (sck.nType == _RELEVANCEFILTERFIRST && !isValidFstDay(pInfo))
	{
		return false;
	}

	if (sck.nType == _RELEVANCEFILTERSECOND)
	{
		int nMarketType = atoi(pInfo->GetMarketType());
		if (nMarketType < 2)
			return false;
	}

	std::bitset<32> bitSubType(sck.nSubType);

	if (bitSubType[0] && !isValidRating(pInfo, currentInfo))
		return false;

	if (bitSubType[1] && !isValidSubType(pInfo, currentInfo))
		return false;

	if (bitSubType[2] && !isValidExpire(sck.nType, pInfo, currentInfo))
		return false;

	if (bitSubType[3] && !isValidIndustry(pInfo, currentInfo))
		return false;

	return true;
}


bool CPrimarySimilarEx::isValidFstDay(const CBondInfo* pInfo)
{
	if (!pInfo || !pInfo->IsValidBond())
		return false;

	time_t tCurrentDay = CBondCalendar::IntToTime(atoi(pInfo->GetIssueStartDate()));
	qb::base::CTime  ctSever(CSSVContainer::GetServerTime());
	ctSever = qb::base::CTime(ctSever.GetYear(), ctSever.GetMonth(), ctSever.GetDay(), 0, 0, 1);

	float fGap = float(ctSever.GetTime() - tCurrentDay);
	fGap /= 24.0 * 60 * 60;
	if (fGap > 0.01 && fGap < 30.01)
	{
		return true;
	}

	return false;
}

bool CPrimarySimilarEx::isValidIssuer(const CBondInfo* pInfo, const  CBondInfo& currentInfo)
{
	if (strlen(pInfo->GetIssuerCode()) <= 0 || strlen(currentInfo.GetIssuerCode()) <= 0)
		return false;
	return strcmp(pInfo->GetIssuerCode(), currentInfo.GetIssuerCode()) == 0;
}

bool CPrimarySimilarEx::isValidIndustry(const CBondInfo* pInfo, const  CBondInfo& currentInfo)
{
	CQBIssuerInfo& IMap = CQBIssuerInfo::instance();
	auto pI = IMap.GetIssuerInfo(currentInfo.GetIssuerCode());
	auto pI2 = IMap.GetIssuerInfo(pInfo->GetIssuerCode());

	if (!pI || !pI2)
		return false;

	//判定一级行业代码
	int nLen1 = strlen(pI->field((int)kIssuerInfo::SW_2021_Sector_Code));
	if (nLen1 <= 0)return false;
	int nLen2 = strlen(pI2->field((int)kIssuerInfo::SW_2021_Sector_Code));
	if (nLen1 != nLen2)return false;

	return strcmp(pI->field((int)kIssuerInfo::SW_2021_Sector_Code), pI2->field((int)kIssuerInfo::SW_2021_Sector_Code)) == 0;
}

bool CPrimarySimilarEx::isValidSubType(const CBondInfo* pInfo, const  CBondInfo& currentInfo)
{
	if (!pInfo || !pInfo->IsValidBond())
		return false;

	if (strcmp(pInfo->GetBondSubType(), currentInfo.GetBondSubType()) == 0)
		return true;

	return false;
}

bool CPrimarySimilarEx::isValidRating(const CBondInfo* pInfo, const CBondInfo& currentInfo) {
	if (!pInfo || !pInfo->IsValidBond())
		return false;

	if (strcmp(pInfo->GetIssuerRating(), currentInfo.GetIssuerRating()) == 0)
		return true;

	return false;
}

bool CPrimarySimilarEx::isValidExpire(int nType, const CBondInfo* pInfo, const CBondInfo& currentInfo)
{
	if (nType != _RELEVANCEFILTERFIRST && nType != _RELEVANCEFILTERSECOND)
		return true;

	if (!pInfo || !pInfo->IsValidBond())
		return false;

	int nExpCurt = 0;
	int nExpTemp = 0;
	if (nType == _RELEVANCEFILTERSECOND)
	{
		if (strlen(currentInfo.GetMaturityDate()) > 0)
			nExpCurt = currentInfo.GetRemainedDays(CSSVContainer::GetServerTime());
		else if (strlen(currentInfo.GetMaturityTerm()) > 0)
			nExpCurt = getTermData(currentInfo.GetMaturityTerm());
		else
			return false;

		nExpTemp = pInfo->GetRemainedDays(CSSVContainer::GetServerTime());
	}
	else
	{// if(GetWndType() == RELEVANCEFILTERFIRST){
		if (strlen(currentInfo.GetMaturityTerm()) <= 0)
			return false;
		nExpCurt = getTermData(currentInfo.GetMaturityTerm());//   CSSTools::IntToTime(atoi(info.GetMaturityDate())) - CSSTools::IntToTime(atoi(info.GetInterestStartDate()));
		//nExpCurt /= (60*60*24);
		nExpTemp = getTermData(pInfo->GetMaturityTerm());//  CSSTools::IntToTime(atoi(pInfo->GetMaturityDate())) - CSSTools::IntToTime(atoi(pInfo->GetInterestStartDate()));
		//nExpTemp /= (60*60*24);
	}
	//else return false;
	int nGap = nExpCurt - nExpTemp;
	if (nExpCurt <= 180)
	{
		if (nGap >= -30 && nGap <= 30) return true;
	}
	else if (nExpCurt > 180 && nExpCurt <= 365)
	{
		if (nGap >= -60 && nGap <= 60) return true;
	}
	else if (nExpCurt > 365 && nExpCurt <= 365 * 3)
	{
		if (nGap >= -60 && nGap <= 60) return true;
	}
	else if (nExpCurt > 365 * 3 && nExpCurt <= 365 * 5)
	{
		if (nGap >= -90 && nGap <= 90) return true;
	}
	else if (nExpCurt > 365 * 5 && nExpCurt <= 3650)
	{
		if (nGap >= -120 && nGap <= 120) return true;
	}
	else
	{
		if (nGap >= -180 && nGap <= 180) return true;
	}
	return false;
}

int CPrimarySimilarEx::getTermData(const char* cTerm)
{
	int nTerm = 0;

	QString strTerm = QString(cTerm);

	if (strTerm.contains("Y"))
	{
		strTerm.replace("Y", "");
		nTerm = strTerm.toInt() * 365;
	}
	else if (strTerm.contains("M"))
	{
		strTerm.replace("M", "");
		nTerm = strTerm.toInt() * 30;
	}
	else if (strTerm.contains("D"))
	{
		strTerm.replace("D","");
		nTerm = strTerm.toInt();
	}
	return nTerm;
}